package com.photoeditor.demo.model.store.module.preload;

import android.support.annotation.NonNull;
import android.text.TextUtils;

import com.common.base.ComponentContext;
import com.common.base.log.RLog;
import com.common.base.utils.DebugUtil;
import com.common.base.utils.ProcessUtils;
import com.common.base.utils.thread.ThreadPool;
import com.photoeditor.demo.app.TestUser;
import com.liulishuo.filedownloader.BaseDownloadTask;
import com.photoeditor.demo.model.event.PreloadResDownloadFinishEvent;
import com.photoeditor.demo.model.filedownload.DownloadListener;
import com.photoeditor.demo.model.filedownload.FileDownloadManager;
import com.photoeditor.demo.model.preference.ChildProcessBroadcastReceiver;
import com.photoeditor.demo.model.preference.SpUtils;
import com.photoeditor.demo.model.store.module.BaseOnlineManager;
import com.photoeditor.demo.model.store.module.StoreOnlineBean;
import com.photoeditor.demo.statistics.StatisticsConstant;
import com.photoeditor.demo.util.FeatureCodeUtil;
import com.photoeditor.demo.util.MD5;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * 预加载管理类<br>
 * 可以用来加载服务器配置状态，通过{@link #getConfig(String)}获取服务器配置信息<br>
 * 可以用来预加载文件资源，通过{@link #getPreloadFile(String)}获取预加载文件<br>
 * Created by edward on 17-10-17.
 */

public class PreloadManager implements BaseOnlineManager.StoreOnlineListener {
    private static final boolean DEBUG = DebugUtil.isDebuggable() || TestUser.isTestUser();
    private static final String TAG = "PreloadManager";
    private PreloadOnlineDataManager mPreloadOnlineDataManager;
    private static PreloadManager sPreloadManager;

    private Map<String, String> mConfig;
    private Map<String, String> mPreloadFiles;
    private Map<String, StoreOnlineBean> mPreloadBeans;

    private boolean mIsFirstPreload = true;

    /**
     * 服务器上的name字段为这个时标识预加载文件，否则视为属性配置
     */
    private static final String PRELOAD_FILE_NAME = "preload-file";

    private Map<String, DownloadListener> mAllPreloadDownloadListener = new HashMap<>();

    private List<BaseOnlineManager.StoreOnlineListener> mListenerList;

    private List<String> mIgnorePreloadFileUrls = new ArrayList<>();
    private static final String SP_KEY_IGNORE_PRELOAD_CONFIG_URL = "SP_KEY_IGNORE_PRELOAD_CONFIG_URL";

    /**
     * 预加载文件的存储路径
     */
    public static final String PRELOAD_FILE_PATH = com.common.base.utils.FileUtil.getExternalCacheDir(
            ComponentContext.getContext(),
            "preload-file", false);
    private static final Object LOCK = new Object();

    private PreloadManager() {
        mConfig = new HashMap<>();
        mPreloadFiles = new HashMap<>();
        mPreloadBeans = new HashMap<>();
        initIgnorePreloadUrl();
        mPreloadOnlineDataManager = new PreloadOnlineDataManager();
        mPreloadOnlineDataManager.addDataListener(this);
    }
    public synchronized static PreloadManager getInstance() {
        if (sPreloadManager == null) {
            sPreloadManager = new PreloadManager();
        }
        return sPreloadManager;
    }

    public void preloadData() {
        ThreadPool.runOnNonUIThread(new Runnable() {
            @Override
            public void run() {
                synchronized (LOCK) {
                    mPreloadOnlineDataManager.loadData();
                    if (mIsFirstPreload) {
                        mIsFirstPreload = false;
                        needUpdateData(true);
                    }
                }
            }
        });
    }


    @Override
    public synchronized void needUpdateData(boolean needUpdate) {
        if (!needUpdate && ProcessUtils.isMainProcess(ComponentContext.getContext())) {
            RLog.d(TAG, "check preload-files...");
            StoreOnlineBean storeOnlineBean = mPreloadOnlineDataManager.getRootBean();
            if (storeOnlineBean == null
                    || storeOnlineBean.getChildModules() == null
                    || storeOnlineBean.getChildModules().size() == 0) {
                return;
            }
            List<StoreOnlineBean> storeOnlineBeanList = storeOnlineBean.getChildModules();
            for (StoreOnlineBean configBean : storeOnlineBeanList) {
                if (TextUtils.equals(configBean.getName(), PRELOAD_FILE_NAME)
                        && !TextUtils.isEmpty(configBean.getRootUrl())
                        && !TextUtils.isEmpty(configBean.getDownUrl())
                        && ProcessUtils.isMainProcess(ComponentContext.getContext())) {
                    downloadFile(configBean);
                }
            }
            notifyDataListener(needUpdate);
            return;
        }
        if (DEBUG) {
            RLog.d(TAG, "start preload...");
        }
        StoreOnlineBean storeOnlineBean = mPreloadOnlineDataManager.getRootBean();
        mConfig.clear();
        mPreloadBeans.clear();
        if (storeOnlineBean == null
                || storeOnlineBean.getChildModules() == null
                || storeOnlineBean.getChildModules().size() == 0) {
            return;
        }
        List<StoreOnlineBean> storeOnlineBeanList = storeOnlineBean.getChildModules();
        for (StoreOnlineBean configBean : storeOnlineBeanList) {
            if (!TextUtils.equals(configBean.getName(), PRELOAD_FILE_NAME)
                    && (configBean.getChildModules() == null
                    || configBean.getChildModules().size() == 0)) {
                // 不是PRELOAD_FILE_NAME且没有挂载子模块就认为是config
                mConfig.put(configBean.getId(), configBean.getName());
                if (configBean.getId().equals("11000")) {
                    //这里非常特殊，名字不是PRELOAD_FILE_NAME，类似于11000这样的bean
                    //就只能读取到name，其他字段没有处理
                    mPreloadBeans.put(configBean.getId(), configBean);
                }
                if (DEBUG) {
                    RLog.d(TAG, "add config: id = " + configBean.getId() + ", value = " + configBean.getName());
                }
            } else if (TextUtils.equals(configBean.getName(), PRELOAD_FILE_NAME)
                    && !TextUtils.isEmpty(configBean.getRootUrl())
                    && !TextUtils.isEmpty(configBean.getDownUrl())
                    && ProcessUtils.isMainProcess(ComponentContext.getContext())) {
                downloadFile(configBean);
            } else {
                // 预加载模块数据
                if (DEBUG) {
                    RLog.d(TAG, "preload-bean: id = " + configBean.getId());
                }
                mPreloadBeans.put(configBean.getId(), configBean);
            }
        }
        notifyDataListener(needUpdate);
    }

    private synchronized void downloadFile(final StoreOnlineBean configBean) {
        if (configBean == null) {
            return;
        }
        // 避免加载不需要的文件，避免因服务器地址切换而重复加载
        if (mIgnorePreloadFileUrls.contains(StoreOnlineBean.removeQueryString(configBean.getDownUrl()))
                || getPreloadFile(configBean.getDownUrl()) != null) {
            return;
        }
        // 预加载文件
        if (DEBUG) {
            RLog.d(TAG, "preload-file: id = " + configBean.getId() + ", value = " + configBean.getDownUrl());
        }
        if (TextUtils.isEmpty(configBean.getDownUrl())) {
            return;
        }

        DownloadListener preListener = mAllPreloadDownloadListener.get(configBean.getDownUrl());
        if (preListener == null) {
            DownloadListener downloadListener = new DownloadListener(configBean.getDownUrl(), PRELOAD_FILE_PATH + File.separator + MD5.getMD5Str(configBean.getDownUrl())) {

                private boolean mIsStarted = false;
                @Override
                public void progress(BaseDownloadTask task, int soFarBytes, int totalBytes) {

                }

                @Override
                public void pending(BaseDownloadTask task, int soFarBytes, int totalBytes) {
                    if (DEBUG) {
                        RLog.e(TAG, "preload-file-pending: url = " + task.getUrl());
                    }
                    com.common.statistics.StatisticsUtils.statisics(StatisticsConstant.UMENG_CUSTOM_EVENT_PRELOAD_DATA_DOWNLOAD,
                            "",
                            "url", StoreOnlineBean.removeQueryString(configBean.getDownUrl()));
                    mIsStarted = true;
                }

                @Override
                public void completed(BaseDownloadTask task) {
                    if (DEBUG) {
                        RLog.d(TAG, "preload-file-completed: url = " + task.getUrl());
                    }
                    mPreloadFiles.put(task.getUrl(), task.getPath());
                    ChildProcessBroadcastReceiver.getInstance().sendBroadcastMsg(
                            ChildProcessBroadcastReceiver.TYPE_PRELOAD_FILE_COMPLETE,
                            new PreloadResDownloadFinishEvent(task.getUrl(), task.getPath())
                    );

                    // 只有启动下载了才需要统计下载成功，应用启动的时候都会尝试下载，如果文件已经下载过了就只会回调completed而不会回调pending
                    if (mIsStarted) {
                        com.common.statistics.StatisticsUtils.statisics(StatisticsConstant.UMENG_CUSTOM_EVENT_PRELOAD_DATA_DOWNLOAD_SUCCESS,
                                "",
                                "url", StoreOnlineBean.removeQueryString(task.getUrl()));
                    }
                    mIsStarted = false;
                }

                @Override
                public void retry(BaseDownloadTask task, Throwable ex, int retryingTimes, int soFarBytes) {
                    if (DEBUG) {
                        RLog.d(TAG, "preload-file-retry: url = " + task.getUrl());
                    }
                    com.common.statistics.StatisticsUtils.statisics(StatisticsConstant.UMENG_CUSTOM_EVENT_PRELOAD_DATA_DOWNLOAD_ERROR,
                            "",
                            "url", StoreOnlineBean.removeQueryString(task.getUrl()),
                            "error-msg", "retry");
                }

                @Override
                public void error(BaseDownloadTask task, Throwable e) {
                    if (DEBUG) {
                        RLog.d(TAG, "preload-file-error: url = " + task.getUrl());
                    }
                    com.common.statistics.StatisticsUtils.statisics(StatisticsConstant.UMENG_CUSTOM_EVENT_PRELOAD_DATA_DOWNLOAD_ERROR,
                            "",
                            "url", StoreOnlineBean.removeQueryString(task.getUrl()),
                            "error-msg", e.getMessage());
                }
            };
            FileDownloadManager.getInstance().addDownloadListener(downloadListener);
            mAllPreloadDownloadListener.put(configBean.getDownUrl(), downloadListener);
        }
        FileDownloadManager.getInstance().startDownload(configBean.getDownUrl(), generateDownloadPathName(configBean.getDownUrl()));
    }

    public static String generateDownloadPathName(String url) {
        return PRELOAD_FILE_PATH + File.separator + MD5.getMD5Str(url);
    }

    /**
     * 根据id获取服务器配置信息
     * @param configId 配置id
     * @return 配置值
     */
    public String getConfig(@NonNull String configId) {
        return mConfig.get(configId);
    }

    /**
     * 获取预加载文件
     * @param fileUrl 文件链接
     * @return 本地文件路径
     */
    public String getPreloadFile(@NonNull String fileUrl) {
        String patternStr = "^http://server\\d?.pefacefecret.info:8080/";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher matcher = pattern.matcher(fileUrl);
        String result;
        // 遍历所有可能的链接，防止因服务器地址更换导致重复加载同一个文件
        if (matcher.find()) {
            for (int i = 0; i < 8; i++) {
                String current = "http://server" + (i == 0 ? "" : String.valueOf(i)) + ".pefacefecret.info:8080/";
                String currentUrl = fileUrl.replaceFirst(patternStr, current);
                if ((result = getPreloadFileInternal(currentUrl)) != null) {
                    return result;
                }
            }
            return null;
        } else {
            return getPreloadFileInternal(fileUrl);
        }
    }

    private String getPreloadFileInternal(@NonNull String fileUrl) {
        fileUrl = FeatureCodeUtil.addFeatureString(fileUrl);
        String result = mPreloadFiles.get(fileUrl);
        if (result == null) {
            File file = new File(PRELOAD_FILE_PATH + File.separator + MD5.getMD5Str(fileUrl));
            if (file.exists() && file.isFile()) {
                result = file.getAbsolutePath();
            }
        }
        return result;
    }


    /**
     * 获取预加载的bean
     * @param beanId
     * @return
     */
    public StoreOnlineBean getPreloadBean(@NonNull String beanId) {
        return mPreloadBeans.get(beanId);
    }

    public synchronized void addDataListener(BaseOnlineManager.StoreOnlineListener listener) {
        if (listener == null) {
            return;
        }
        if (mListenerList == null) {
            mListenerList = new CopyOnWriteArrayList<>();
        }
        mListenerList.add(listener);
    }

    public synchronized void removeListener(BaseOnlineManager.StoreOnlineListener listener) {
        if (mListenerList == null || listener == null) {
            return;
        }
        mListenerList.remove(listener);
    }

    protected synchronized void notifyDataListener(final boolean needUpdate) {
        ThreadPool.runUITask(new Runnable() {
            @Override
            public void run() {
                if (mListenerList == null) {
                    return;
                }
                for (BaseOnlineManager.StoreOnlineListener listener : mListenerList) {
                    listener.needUpdateData(needUpdate);
                }
            }
        });
    }

    /**
     * 保存已使用过的一次性文件的链接防止用户使用清理软件清理我们的数据之后重复下载，如拼图引导视频
     * @param url
     */
    public void addIgnorePreloadUrl(String url) {
        mIgnorePreloadFileUrls.add(StoreOnlineBean.removeQueryString(url));
        StringBuilder allConfigIdBuilder = new StringBuilder();
        for (String currentUrl : mIgnorePreloadFileUrls) {
            allConfigIdBuilder.append(currentUrl).append(",");
        }
        SpUtils.obtain().save(SP_KEY_IGNORE_PRELOAD_CONFIG_URL, allConfigIdBuilder.toString());
    }

    private void initIgnorePreloadUrl() {
        String urlStrings = SpUtils.obtain().getString(SP_KEY_IGNORE_PRELOAD_CONFIG_URL);
        if (TextUtils.isEmpty(urlStrings)) {
            return;
        }
        String[] urls = urlStrings.split(",");
        for (String url : urls) {
            if (!TextUtils.isEmpty(url)) {
                mIgnorePreloadFileUrls.add(url);
            }
        }
    }

}
